package com.exercise.rpc.client.proxy;/**
 * Created by kete on 2018-10-29.
 */

import com.exercise.rpc.client.ClientHandler;
import com.exercise.rpc.client.ConnectManage;
import com.exercise.rpc.client.RpcFuture;
import com.exercise.rpc.common.model.RpcRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.UUID;

/**
 * 作者：kete
 * 创建时间：2018-10-29 15:23
 * 功能描述：
 * 版本：
 */
public class ObjectProxy implements InvocationHandler {

    private static final Logger logger = LoggerFactory.getLogger(ObjectProxy.class);
    private Class<?> clazz;

    public ObjectProxy(Class<?> clazz) {
        this.clazz = clazz;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class == method.getDeclaringClass()) {
            String name = method.getName();
            if ("equals".equals(name)) {
                return proxy == args[0];
            } else if ("hashCode".equals(name)) {
                return System.identityHashCode(proxy);
            } else if ("toString".equals(name)) {
                return proxy.getClass().getName() + "@" +
                        Integer.toHexString(System.identityHashCode(proxy)) +
                        ", with InvocationHandler " + this;
            } else {
                throw new IllegalStateException(String.valueOf(method));
            }
        }

        RpcRequest request = createRequest(method.getDeclaringClass().getName(),method.getName(),args);

        ClientHandler handler = ConnectManage.me().chooseHandler();

        RpcFuture future = handler.sendMsg(request);

        return future.get();
    }

    private RpcRequest createRequest(String className, String methodName, Object[] args) {
        RpcRequest request = new RpcRequest();
        request.setRequestId(UUID.randomUUID().toString());
        request.setInterfaceName(className);
        request.setMethodName(methodName);
        request.setParameters(args);

        Class[] parameterTypes = null;
        if(args!=null){

            parameterTypes = new Class[args.length];
            // Get the right class type
            for (int i = 0; i < args.length; i++) {
                parameterTypes[i] = getClassType(args[i]);
            }


            //print parameter
            for (int i = 0; i < args.length; ++i) {
                logger.debug(args[i].toString());
            }

            for (int i = 0; i < parameterTypes.length; ++i) {
                logger.debug(parameterTypes[i].getName());
            }

            request.setParameterTypes(parameterTypes);
        }

        logger.debug(className);
        logger.debug(methodName);

        return request;
    }

    private Class<?> getClassType(Object obj) {
        Class<?> classType = obj.getClass();
        String typeName = classType.getName();
        switch (typeName) {
            case "java.lang.Integer":
                return Integer.TYPE;
            case "java.lang.Long":
                return Long.TYPE;
            case "java.lang.Float":
                return Float.TYPE;
            case "java.lang.Double":
                return Double.TYPE;
            case "java.lang.Character":
                return Character.TYPE;
            case "java.lang.Boolean":
                return Boolean.TYPE;
            case "java.lang.Short":
                return Short.TYPE;
            case "java.lang.Byte":
                return Byte.TYPE;
        }

        return classType;
    }
}
